﻿using System;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;

namespace FireFly
{
    /// <summary>
    /// Relay command base class
    /// </summary>
    /// <typeparam name="TViewModel">type of ViewModel</typeparam>
    /// <typeparam name="TContext">type of UIContext of CommandExecutionContext</typeparam>
    public class RelayCommand<TViewModel, TContext> : IRelayCommand 
        where TViewModel: IViewModel
    {
        Action<CommandExecutionContext<TViewModel, TContext>> _execute;
        Predicate<CommandExecutionContext<TViewModel, TContext>> _canExecute;
        string _cmdKey;

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="execute">execution logic</param>
        /// <param name="canExecute">predicate which allow or decline command execution</param>
        /// <param name="cmdKey"></param>
        public RelayCommand(Action<CommandExecutionContext<TViewModel, TContext>> execute, Predicate<CommandExecutionContext<TViewModel, TContext>> canExecute, string cmdKey)
        {
            _execute = execute;
            _canExecute = canExecute;
            _cmdKey = cmdKey;
        }

        /// <summary>
        /// Constructor for command  that is always executable
        /// </summary>
        public RelayCommand(Action<CommandExecutionContext<TViewModel, TContext>> execute, string cmdKey)
        {
            _execute = execute;
            _cmdKey = cmdKey;
        }

        /// <summary>
        /// See <see cref="System.Windows.Input.ICommand"/> for details
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            var res = (_canExecute != null) ? _canExecute(ViewModelService.Instance.GetCommandContext(this, parameter)) : true;
            return res;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            try
            {
                if(!CanExecute(parameter))
                    throw new CommandExecutionException("Trying to execute unavailable command.", this);
                _execute(ViewModelService.Instance.GetCommandContext(this, parameter));
            }
            catch(Exception err)
            {
                ViewModelService.Instance.ProcessException(err);
            }
        }
                
        public ImageSource Icon
        {
            get
            {
                return ViewModelService.Instance.GetIcon(_cmdKey);
            }
        }

        public virtual string Caption
        {
            get
            {
                return ViewModelService.Instance.GetString(_cmdKey);
            }
        }

        public MenuItem CreateMenuItem(ContextMenu menu, object commandParameter)
        {
            var item = new MenuItem()
            {
                Header = Caption,
                Tag = menu,
                Icon = new Image()
                {
                    Width = 16,
                    Height = 16,
                    Source = this.Icon
                }
            };
            item.CommandParameter = commandParameter ?? item;
            item.Command = this;

            return item;
        }

        #region IRelayCommand Members

        public void UpdateCanExecute()
        {
            CanExecuteChanged(this, null);
        }

        #endregion
    }
}